<?php
namespace app\system\controller;
use app\system\model\Admin as AdminModel;
use app\system\model\AdminFunc as AdminFuncModel;
use app\system\model\AdminRankFunc as AdminFuncRelationModel;
use app\system\model\AdminUser as AdminUserModel;
use app\system\model\SystemSafe as SystemSafeModel;
use app\system\model\SystemSafeUrl as SystemSafeUrlModel;
use think\facade\Cache;
use think\facade\Request;
use yichenthink\utils\ReturnMsg;
use yichenthink\utils\Url;

class Base extends \app\Base {
	protected $inlet = "/system.php"; //放行的支持入口文件
	protected $access = 'system'; //入口标识 ==入口文件名即可
	protected $param = []; //请求数据 如果设置了拦截过滤请求数据,请使用该属性来获取过滤后的数据
	protected $isAdmin = false; //是否系统超级管理员
	protected $safeFunc = []; //操作员使用权限功能表

	protected $safe = [
		"grade_id" => 0, //防御级别
		"user_id" => null, //不受限制的系统管理人员 id
		"cache_time" => 0, //更新过滤设置的缓存时间
	];
	protected $path = 'index/login'; //=由于斜杠在数据库里有时候匹配不出来，所以这里需要把path请求地址斜杠替换后的;
	protected function initialize() {

		// 仅支持admin.php入口进行访问。
		$path = Request::server()['SCRIPT_NAME'];
		if (substr_compare($path, $this->inlet, -strlen($this->inlet)) !== 0) {
			ReturnMsg::returnMsg(400, "非法请求,请使用，http://" . Request::host() . $this->inlet . "进行访问");
		}
		// Cache::clear(); //清空缓存用于测试时候实时
		// 继承父类
		parent::initialize();
		// 获取当前地址栏不含域名后缀和参数 用0替换斜杠
		$this->path = Url::replaceStr(Request::path());
		// $this->path = Request::path();
		$this->safeUrl();
	}

	// 验证url地址是否需要登陆鉴权
	protected function safeUrl() {
		$path = $this->path;
		$safeUrl = Cache::get($this->access . 'safeUrl');
		if (empty($safeUrl)) {
			// 放行不需要登录就可以访问的url
			$safe = SystemSafeUrlModel::where('access', $this->access)->select();
			$func = [];
			if ($safe) {
				foreach ($safe as $key => $value) {
					$func[Url::replaceStr($value['url'])] = $value; //转小写用.替换斜杠
				}
				Cache::set($this->access . 'safeUrl', $func, 10);
				$safeUrl = $func;
			}
		}
		if (isset($safeUrl[$path]) && $safeUrl[$path]['state'] == 0) {
			// 放行所有不需要登陆就能访问的url
		} else {
			// 需要验证登陆状态的地址继续验证执行safeHold验证拦截
			$this->safeHold();
		}
		// ReturnMsg::returnMsg(401.1, '授权失效或已过期,请重新登陆', $safeUrl);
	}

	// 逐步进行拦截验证
	protected function safeHold() {
		// 拦截未登陆的用户
		$this->validateLogin();
		//获取后台安全防御设置信息,限制已登陆的管理人员部分功能； 缓存若不存在数据库获取并存入缓存,一般在调试完毕后，上架运营状态下，缓存时间可设置1天以上。
		$this->safe = Cache::get($this->access . 'safe');

		if (!$this->safe) {
			$safe = SystemSafeModel::field(['grade_id', 'user_id', 'cache_time'])->find();
			// ->visible(['grade_id', 'user_id', 'cache_time']);

			if (isset($safe['cache_time'])) {
				$this->safe = $safe;
				if (!empty($safe['cache_time'])) {
					Cache::set($this->access . 'safe', $safe, $safe['cache_time']);
				}

			} else {
				ReturnMsg::returnMsg(401.1, '未导入安全拦截数据,请导入后访问', $safe);
			}

		}
		// 系统创始人略过后续验证步骤
		if ($this->safe['user_id'] === $this->userInfo['uid']) {
			$this->isAdmin = true;
		}
		//获取用户基本资料
		$this->safeFunc = Cache::get($this->access . 'admin' . $this->userInfo['uid']);

		if (!$this->safeFunc) {
			$safeFunc = $this->adminAuth();
			if (is_array($safeFunc)) {
				$this->safeFunc = $safeFunc;
				Cache::set($this->access . 'admin' . $this->token['uid'], $safeFunc);
			} else {
				ReturnMsg::returnMsg(401.1, '未创建功能接口,请创建后访问', $safeFunc);
			}

		}
		//根据安全设置进行,对已登陆的用户,进行部分功能操作访问权限拦截
		$this->guard();
	}

	protected function validateLogin() {
		$login = Cache::get('login' . $this->userInfo['uid']);

		if (!$login || !isset($login['token']) || $login['expires'] < time()) {
			ReturnMsg::returnMsg(401.1, '请求未授权,或已过期,请重新登陆', $login);
		}
		if ($login['token'] !== $this->token['access_token']) {
			ReturnMsg::returnMsg(401.1, '授权失效或已过期,请重新登陆', $login);
		}
	}

	/*
		* 这里可用用redis缓存登陆状态
		*获取用户权限，和拥有的功能方法 赋值给$this->admin;
	*/
	protected function adminAuth() {

		if ($this->isAdmin) {
			// 超级管理员
			$funcList = AdminFuncModel::select()
				->visible(['ban_param', 'only_param', 'action', 'method', 'access']);
		} else {
			// // 普通管理员-查询授权可访问的功能限制信息
			$list = AdminModel::with(['adminGroupFunc.adminFunc'])
				->where('id', $this->userInfo['uid'])
				->visible(['admin_group_func' => ['ban_param', 'only_param', 'action', 'method', 'access']])
				->find();
			$funcList = isset($list['admin_group_func']) ? $list['admin_group_func'] : [];

		}
		$func = [];
		foreach ($funcList as $key => $value) {
			if ($value['access'] == $this->access) {
				$func[Url::replaceStr($value['action'])] = $value; //转小写用.替换斜杠
			}

		}
		return $func;

		# code...
	}
	// 验证安全级别 根据不同级别执行进行相关授权验证
	protected function guard() {
		if ($this->userInfo['uid'] != $this->safe['user_id'] && $this->safe['grade_id']) {
			switch ($this->safe['grade_id']) {
			case 0:
				break;
			case 1:
				$this->guardUrl(); //拦截不支持当前用户访问的url地址
				break;
			case 2:
				$this->guardUrl(); //拦截不支持当前用户访问的url地址
				$this->guardMethod(); //拦截不支持的请求方法
				break;
			case 3:
				$this->guardUrl(); //拦截不支持当前用户访问的url地址
				$this->param = $this->guardParam(Request::param());
				// $this->guardParam(); //过滤不支持的请求参数
				break;
			default:
				$this->guardUrl(); //拦截不支持当前用户访问的url地址
				$this->guardMethod(); //拦截不支持的请求方法
				// $this->guardParam(); //过滤不支持的请求参数
				$this->param = $this->guardParam(Request::param());
				# code...
				break;
			}

		};
	}
	/*
		*	拦截不支持当前用户访问的url地址
	*/
	protected function guardUrl() {
		$data = [];
		$data[] = $this->userInfo['uid'];
		// // 获取当前地址栏不含域名后缀和参数 用0替换斜杠
		// $path = str_replace("/", "0", Request::path());
		$path = $this->path;
		if (!isset($this->safeFunc[$path])) {
			ReturnMsg::returnMsg(401.4, 'url请求未授权');
		}

	}
	/*
		*	拦截不支持的请求方法
		*	控制器里读取请求数据是使用该方法进行过滤即可  例如： $param=$this->guardParam(Request::param());
	*/
	protected function guardMethod() {
		$path = $this->path;
		if (isset($this->safeFunc[$path]['method']) && $this->safeFunc[$path]['method'] != Request::method()) {
			ReturnMsg::returnMsg(401.4, '不支持的请求方法');
		}
	}
	/*
		*	过滤不支持的请求参数
	*/
	protected function guardParam($params = []) {
		$path = $this->path;
		// $params = Request::param();
		if (isset($this->safeFunc[$path]['only_param'])) {
			// 保留支持的参数key 删除所有不支持的key
			$param = [];
			$only_param = explode(",", $this->safeFunc[$path]['only_param']);
			foreach ($only_param as $key => $value) {
				if (isset($params[$value])) {
					$param[$value] = $params[$value];
				}
			}
		} elseif (isset($this->safeFunc[$path]['ban_param'])) {
			// 仅删除不支持的参数key
			$param = $params;
			$ban_param = explode(",", $this->safeFunc[$path]['ban_param']);
			foreach ($ban_param as $key => $value) {
				if (isset($param[$value])) {
					unset($param[$value]);
				}
			}
		}
		return $param;
	}
}
